package domain;

import utils.CommandAnalyzer;
import enums.Frequency;
import enums.LoanState;
import basic.Date;
import basic.Transaction;

//@author A0103947A
public class Loan extends Transaction {
	
	public static final double DEFAULT_INTERESTRATE = 0;
	
	//attributes
	private Date _dueDate;
	private LoanState _loanState;
	private int _monthsLeft;
	private double _paymentPerMonth;
	private double _interestRate;
	private double _gainFromInterest;
	

	/**
	 * 
	 */
	public Loan()
	{	
		super();
		_dueDate = new Date();
		_loanState = LoanState.NOT_DEFINED;
		_monthsLeft = calculateMonthsLeft(_date, _dueDate);
		_paymentPerMonth = _amount/_monthsLeft;
		_interestRate = DEFAULT_INTERESTRATE;
		_partyInvolved = DEFAULT_PARTYINVOLVED;
		_frequency = Frequency.MONTHLY;
		_gainFromInterest = 0;
		
	}
	
	/**
	 * @param loan
	 */
	public Loan(Loan loan)
	{
		this.set(loan);
	}
	/**
	 * 
	 * @param loan
	 * @Precondition 
	 * @Postcondition 
	 */
	public void set(Loan loan)
	{
		super.set(loan);
		this.set_loanState(loan._loanState);
		this.set_dueDate(loan._dueDate);
		this.set_monthsLeft(loan._monthsLeft);
		this.set_paymentPerMonth(loan._paymentPerMonth);
		this.set_interestRate(loan._interestRate);
		this.set_partyInvolved(loan._partyInvolved);
		//System.out.println("set_gainFromInterest occurs in set(Loan loan)");
		this.set_gainFromInterest(loan._gainFromInterest);
	}

	
	/**
	 * 
	 * @param startDate
	 * @param dueDate
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public int calculateMonthsLeft(Date startDate, Date dueDate) {
		int monthsRemaining = 0;
		
		int startMonth = startDate.get_month();
		int startYear = startDate.get_year();
		int dueMonth = dueDate.get_month();
		int dueYear = dueDate.get_year();
		
		if (dueYear > startYear && dueMonth >= startMonth) 
			monthsRemaining = (dueYear-startYear) * 12 + (dueMonth - startMonth);
		else if (dueYear > startYear && dueMonth < startMonth)
			monthsRemaining = (dueYear - startYear - 1) * 12 + (12 - dueMonth - startMonth);
		else if (dueYear == startYear)
			monthsRemaining = dueMonth - startMonth;
		
		return monthsRemaining;
	}
	
	/**
	 *
	 * @see java.lang.Object#toString()
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public String toString()
	{

		return _name + "," + _amount + "," + _loanState + "," + _id
				+ "," + CommandAnalyzer.PARAM_DUEDATE + _dueDate.toString() 
				+ "," + CommandAnalyzer.PARAM_MEMBER + _byMem 
				+ "," + CommandAnalyzer.PARAM_DATE + _date.toString() 
				+ "," + CommandAnalyzer.PARAM_COMMENTS + _comments 
				+ "," + CommandAnalyzer.PARAM_PARTYINVOLVED + _partyInvolved
				+ "," + CommandAnalyzer.PARAM_TOTALAMOUNT + _totalAmount
				+ "," + CommandAnalyzer.PARAM_UPDATEDATE + _lastUpdateDate
				+ "," + CommandAnalyzer.PARAM_INTEREST + _interestRate;
	}
	
	//getter and setters
	public Date get_dueDate() {
		return _dueDate;
	}

	public void set_dueDate(Date dueDate) {
		if(!dueDate.toString().equals(Date.NA_DATE))
			_dueDate = dueDate;
	}

	public LoanState get_loanState() {
		return _loanState;
	}

	public void set_loanState(LoanState borrowOrLend) {
		this._loanState = borrowOrLend;
	}
	
	public int get_monthsLeft() {
		return this._monthsLeft;
	}
	
	public void set_monthsLeft(int timeRemaining) {
		this._monthsLeft = timeRemaining;
	}
	
	public double get_paymentPerMonth() {
		return this._paymentPerMonth;
	}
	
	/**
	 * 
	 * @param startDate
	 * @param dueDate
	 * @Precondition _monthsLeft is set to a previous value, before either/both startDate and dueDate have been changed
	 * @Postcondition calculates the new value of monthsRemaining after either/both startDate and dueDate have been changed
	 */
	public void set_monthsRemaining(Date startDate, Date dueDate) {
		int monthsRemaining = 0;
		
		int startMonth = startDate.get_month();
		int startYear = startDate.get_year();
		int dueMonth = dueDate.get_month();
		int dueYear = dueDate.get_year();
		
		if (dueYear > startYear && dueMonth >= startMonth) 
			monthsRemaining = (dueYear-startYear) * 12 + (dueMonth - startMonth);
		else if (dueYear > startYear && dueMonth < startMonth)
			monthsRemaining = (dueYear - startYear - 1) * 12 + (12 - dueMonth - startMonth);
		else if (dueYear == startYear)
			monthsRemaining = dueMonth - startMonth;
		
		this._monthsLeft = monthsRemaining;
		
		if(_monthsLeft!=0)
		{
			this._paymentPerMonth = _totalAmount/_monthsLeft;
		}
		else
		{
			_paymentPerMonth = _totalAmount;
		}
	}
	
	public double get_interestRate() {
		return _interestRate;
	}
	public void set_interestRate(double interestRate) {
		if(_interestRate != CommandAnalyzer.NA_INTEREST)
			this._interestRate = interestRate;
	}

	public void set_paymentPerMonth(double paymentPerMonth) {
		this._paymentPerMonth = paymentPerMonth;
	}
	
	/**
	 * 
	 * @param 
	 * @return 
	 * @Precondition 
	 * @Postcondition loan calculates payment per month by dividing total amount by months left; if loan is an investment, payment
	 * 	              per month is set to 0
	 */
	public void set_paymentPerMonth() {
		if (this.get_loanState() == LoanState.INVESTMENT) {
			this._paymentPerMonth = 0;
			return;
		}
		else {			
			if(_monthsLeft!=0)
			{
				_paymentPerMonth = this._totalAmount/this._monthsLeft;
			}
			else
			{
				_paymentPerMonth = 0;
			}
		}	
	}
	
	/**
	 * 
	 * @param 
	 * @param 
	 * @return 
	 * @Precondition loan has a defined _amount but not a defined _totalAmount
	 * @Postcondition loan calculates total value at a given interest rate over time, shows what the final amount of the loan 
	 * 				   will be when we get to the due date
	 */
	public void updateTotalAmount() {
		if (this._loanState == LoanState.INVESTMENT) {
			this._totalAmount = this._amount;
		}
		else if (this._loanState == LoanState.BORROWED || this._loanState == LoanState.LENT) {
			int yearsLeft;
			if (this._monthsLeft % 12 == 0) {
				yearsLeft = this._monthsLeft/12;
			}
			else 
				yearsLeft = this._monthsLeft/12 + 1;
			
			this._totalAmount = this._amount * Math.pow((1+this._interestRate/100), yearsLeft);
			
		}
	}
	
	/**
	 * 
	 * @param 
	 * @param 
	 * @return _gainFromInterest
	 * @Precondition 
	 * @Postcondition returns _gainFromInterest to calling function
	 */
	public double get_gainFromInterest() {
		return _gainFromInterest;
	}

	/**
	 * 
	 * @param double value
	 * @param 
	 * @return 
	 * @Precondition 
	 * @Postcondition sets _gainFromInterest to input value
	 */
	public void set_gainFromInterest(double value) {
		if (this._loanState == LoanState.BORROWED) {
			this._gainFromInterest = Math.abs(value)*-1;
		}
		else 
			this._gainFromInterest = value;
	}
	
}